#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <pthread.h>


#include <libhiseg.h>

#define FRUN	0
#define FEXIT	1

static int      exit_flag;

const char * MODULE_NAME = "HISEG_MACHINE_V100";

const char * VISION_START = "\n\t"
 "  _______________________________________________\n\t"
 " |                                               |\n\t"
 " |   %s                          |\n\t"
 " |                                               |\n\t"
 " |   Build:   %s %s               |\n\t"
 " |                                               |\n\t"
 " |   (C) Copyright 2007-2009 feinno, Inc         |\n\t"
 " |            All Rights Reserved.               |\n\t"
 " |_______________________________________________|\n\n";

const char * VISION_END = "\n\t"
 "  _______________________________________________\n\t"
 " |                                               |\n\t"
 " |   %s                          |\n\t"
 " |                                               |\n\t"
 " |   Build:   %s %s               |\n\t"
 " |                                               |\n\t"
 " |   (C) Copyright 2007-2009 feinno, Inc         |\n\t"
 " |            All Rights Reserved.               |\n\t"
 " |_______________________________________________|\n\n";

const char * VISION_USAGE = "usage: %s -D <dictpath> -s <srcpath> -d <dstpath>"
 " \n\t\t -U <userdict> -D <dictpath>"
 " \n\t\t -t <threadnum> -s <srcdir> -d <dstdir> -D <dictpath>"
 " \n\n\n";

//on signal
void  signal_handler( int sig_num )
{
	sigset_t  newmask,oldmask;
	sigemptyset(&newmask);
	sigaddset(&newmask, sig_num);
	sigprocmask(SIG_BLOCK, &newmask, &oldmask );

	switch ( sig_num )
	{
	case SIGUSR2: //exit
		printf("***Signal [SIGUSR2] received, System exit...\n" );
		exit_flag = exit_flag == FRUN ? FEXIT: exit_flag;
		//before exit		
		break;
	case SIGINT:
		printf("***Signal [SIGINT] received, System exit...\n" );
		exit_flag = exit_flag == FRUN ? FEXIT: exit_flag;
		//before exit		
		break;
	default:
		printf("***Signal [%d] received, Ignored...\n", sig_num );
		break;
	}
  	signal(sig_num, signal_handler);
  	sigprocmask(SIG_SETMASK, &oldmask, NULL);
  	return;
}

#define SRC_FILE_MAX	1024
#define PATH_LEN		128
#define THREAD_NUM_MAX	255
#define FILE_SIZE_MAX	(1<<14)

typedef struct tconf{
	const char * dst_path;
	unsigned short num;			//num of src_file
	char src_files[SRC_FILE_MAX][PATH_LEN];
} tconf;

//fetch all files path to tconf
int fetch_files(const char * path, tconf * t)
{
	DIR * pdir = opendir(path);
	if(pdir==NULL){
		fprintf(stderr, "can't open %s as a dir\n", path);
		return -1;
	}
	t->num = 0;
	struct dirent * pdirent;
	struct stat f_ftime;
	for(pdirent=readdir(pdir);pdirent!=NULL;pdirent=readdir(pdir)){
		printf("file: %s\n",pdirent->d_name);
		if(strcmp(pdirent->d_name,".")==0||strcmp(pdirent->d_name,"..")==0) continue;

		snprintf(t->src_files[t->num], PATH_LEN, "%s/%s", path, pdirent->d_name);
		if(stat(t->src_files[t->num], &f_ftime)!=0) return -2 ;
		if(S_ISDIR(f_ftime.st_mode)) continue; /*skip sub dir*/
		t->num ++;
		if(t->num == SRC_FILE_MAX){
			fprintf(stderr, "too many files\n");
			break;
		}
	}
	printf("file num: %d\n",t->num);
	closedir(pdir);
	return 0;
}

int seg(const char * src, const char * dst, unsigned long s)
{
	FILE *srcp = fopen(src, "r");
	if(srcp == NULL){
		fprintf(stderr, "open %s err\n", src);
		return -1;
	}
//	printf("to handle file %s [%lu] \n", src, (long)pthread_self());
	if(fseek(srcp, 0, SEEK_END) < 0){
		return -3;
	}
	unsigned int size = ftell(srcp);
	if(size <= 0){
		fprintf(stderr, "ftell %s err\n", src);
		return -4;
	}
	char * buf = malloc(size+1);
	if(fseek(srcp, 0, SEEK_SET) < 0){
		return -5;
	}
	if(fread(buf, size, 1, srcp) != 1){
		fprintf(stderr, "read %u/%u\n", strlen(buf), size);
		return -6;
	}
	buf[size] = 0;
	FILE *dstp = fopen(dst, "a+");
	if(dstp == NULL){
		fprintf(stderr, "open %s err\n", dst);
		return -7;
	}
	int ret = hiseg_do(s, buf, size);
	if(ret < 0){
		fprintf(stderr, "hiseg_do err, ret=%d\n", ret);
		return -8;
	}

	int len = 0;
	char out[1024] = {0};
	while((len = hiseg_next_res(s, out, 1024)) > 0){
		if(len > 1024){
			fprintf(stderr, "token too long %s\n", out);
			continue;
		}
		out[len] = 0x20;
		out[len + 1] = 0;
		fputs(out, dstp);
	}
//	printf("handle file %s [%lu] \n", src, (long)pthread_self());
	free(buf);
	fclose(srcp);
	fclose(dstp);
	return size;
}

int on_cmd(tconf * t)
{
	unsigned long s = get_segger();
	if(s == 0){
		return -1;
	}
	int ret = seg(t->src_files[0], t->dst_path, s);
	if(ret < 0){
		fprintf(stderr, "seg filed, ret = %d\n", ret);
		return -2;
	}
	return_segger(s);
	return 0;
}

void* on_seg_th(void* arg)
{
	pthread_detach( pthread_self() );
    printf("[system]th_do: thread [%lu:%lu] start up\n", (long)getpid(), (long)pthread_self() );
	tconf * t = (tconf *)arg;

	unsigned long s = get_segger();
	if(s == 0){
		goto EXIT;
	}
	char dst_path[PATH_LEN];
	snprintf(dst_path, PATH_LEN - 1, "%s/%lu", t->dst_path, pthread_self());
	int ret = mkdir(dst_path, S_IRWXU);
/*	if(ret < 0 && ret != EEXIST){
		fprintf(stderr, "mkdir, ret = %d\n", ret);
		goto EXIT;
	}
*/
	char stat_path[PATH_LEN];
	snprintf(stat_path, PATH_LEN - 1, "%s/%lu/0", t->dst_path, pthread_self());
	FILE *statf = fopen(stat_path, "a+");
	if(statf == NULL){
		fprintf(stderr, "open %s err\n", stat_path);
		goto EXIT;
	}
	unsigned int i = 0;		//the file name
	int num = 0;
	unsigned long long total = 0;	//total size
	char stat_size[32];
	while(exit_flag == FRUN){
		if(total >= FILE_SIZE_MAX * i){
			printf("%llu/%u\n", total, FILE_SIZE_MAX * i);
			i ++;
			snprintf(dst_path, PATH_LEN - 1, "%s/%lu/%u", t->dst_path, (long)pthread_self(), i);
		}
		ret = seg(t->src_files[num++], dst_path, s);
		if(ret < 0){
			fprintf(stderr, "seg filed, ret = %d\n", ret);
			goto EXIT;
		}
		total += ret/1024;
		snprintf(stat_size, 31, "%llu\n", total);
		ret = fputs(stat_size, statf);
		fflush(statf);
		if(ret < 0){
			fprintf(stderr, "stat filed, ret = %d\n", ret);
			goto EXIT;
		}
		if(num == t->num){
			num = 0;
		}
	}
	fclose(statf);
EXIT:
	return_segger(s);
	exit_flag ++;
    printf("[system]th_do: thread [%lu:%lu] shut down\n", (long)getpid(), (long)pthread_self() );
	return NULL;
}

int on_threads(int num, void *(*th)(void*), tconf * t)
{
	int ret = 0;
	pthread_t tid;

	int i;
	for(i=0; i<num; i++){
		ret = pthread_create(&tid, NULL, th, t);
		if (ret != 0){
			printf("[system]init_threads: thread[%d] start failed, ret = %d\n", i, ret );
			return -1;
		}
//		printf("[system]init_threads: %ld[%d] ok\n", (long)tid, i);
	}
	printf("[system]init_threads: all of %d threads startup\n", num);
	return 0;
}

int main(int argc,char* argv[])
{
	int			ret;

	printf( "\n" );
	printf( "\n" );
	fflush( stdout );
	printf(VISION_START, MODULE_NAME, __TIME__, __DATE__);

	int thread_num = 0;
    const char * dict_path = NULL;
    const char * dict_utf8 = NULL;
    const char * src_path = NULL;
    const char * dst_path = NULL;
	while( ( ret = getopt( argc, argv, "s:d:U:D:t:h" ) ) != EOF ){
		switch( ret ){
			case 's':
				src_path = optarg;
				break;
			case 'd':
				dst_path = optarg;
				break;
			case 'D':
				dict_path = optarg;
				break;
			case 'U':
				dict_utf8 = optarg;
				break;
			case 't':
				thread_num = atoi(optarg);
				if(thread_num > THREAD_NUM_MAX){
					fprintf(stderr, "thread num max is %d\n", THREAD_NUM_MAX);
					exit(1);
				}
				break;
			case 'h':
				fprintf(stdout, VISION_USAGE, argv[0]);
				return 0;
			default:
				fprintf(stdout, VISION_USAGE, argv[0]);
				exit(2);
		}
	}
	if(dict_utf8 != NULL && dict_path != NULL){//gendict
		ret = gen_dict(dict_utf8, dict_path);
		if(ret < 0){
			fprintf(stderr, "[system]gendict %s err, ret=%d\n", dict_utf8, ret);
			exit(3);
		}
		fprintf(stdout, "[system]gen dict\n");
		exit(0);
	}
	//init
	if(dict_path == NULL){
		fprintf(stdout, VISION_USAGE, argv[0]);
		exit(4);
	}
	ret = init(dict_path);
   	if(ret < 0){
		fprintf(stderr, "[system]init hiseg err, dictpath %s ret=%d\n", dict_path, ret);
		exit(5);
   	}

	signal(SIGUSR1, signal_handler);
	signal(SIGUSR2, signal_handler);
	signal(SIGTERM, signal_handler);
	signal(SIGINT,  signal_handler);//Ctrl+C
	signal(SIGPIPE, signal_handler);
	signal(SIGALRM, signal_handler);
	signal(SIGHUP,  signal_handler);
	signal(SIGQUIT, signal_handler);
	signal(SIGCHLD, signal_handler);

	if(src_path == NULL || dst_path == NULL){
		fprintf(stdout, VISION_USAGE, argv[0]);
		exit(6);
	}
	tconf t;
	t.dst_path = dst_path;
	if(thread_num == 0){
		strncpy(t.src_files[0], src_path, PATH_LEN);
		t.num = 1;
		ret = on_cmd(&t);
		if(ret < 0){
			fprintf(stderr, "[system]on cmd failed, ret=%d\n", ret);
			exit(7);
		}
	}else{
		ret = fetch_files(src_path, &t);
		if(ret < 0){
			fprintf(stderr, "[system]fetch files, ret=%d\n", ret);
			exit(8);
		}
		ret = on_threads(thread_num, on_seg_th, &t);
		if(ret < 0){
			fprintf(stderr, "[system]on threads failed, ret=%d\n", ret);
			exit(9);
		}
		while(exit_flag != FEXIT+thread_num){
			usleep(100*1000);
		}
	}

	destroy();

	printf(VISION_END, MODULE_NAME, __TIME__, __DATE__);
	return 0;
}
